package file

import (
	"bufio"
	"bytes"
	"fmt"
	"io"
	"mime/multipart"
	"net/http"
	"os"
	"strings"
	"time"
)

// 将字符串转换为io.Reader
func StrToReader(str string) *strings.Reader {
	return strings.NewReader(str)
}

// 将字符串转换为byte
func StrToByte(str string) []byte {
	return []byte(str)
}

// byte转字符串
func ByteToStr(b []byte) string {
	return string(b)
}

// 将byte转换为io.Reader
func ByteToReader(b []byte) *strings.Reader {
	return strings.NewReader(string(b))
}

// 将io.Reader转换为byte
func ReaderToByte(r *strings.Reader) []byte {
	b := make([]byte, r.Len())
	read, err := r.Read(b)
	if err != nil {
		return nil
	}
	if read != len(b) {
		return nil
	}
	return b
}

// 读取文件全部内容返回string
func ReadFile(filePath string) string {
	file, err := os.OpenFile(filePath, os.O_RDONLY, 0666)
	if err != nil {
		panic(err)
	}
	defer file.Close()
	b, err := io.ReadAll(file)
	if err != nil {
		panic(err)
	}
	return string(b)
}

func ReadFileHead(filePath string, num int) string {
	file, err := os.OpenFile(filePath, os.O_RDONLY, 0666)
	if err != nil {
		panic(err)
	}
	defer file.Close()
	r := bufio.NewReader(file)
	var str strings.Builder
	//判断长度是否越界
	if num < 1 {
		num = 1
	}
	for i := 0; i < num; i++ {
		line, _, err := r.ReadLine()
		if err == io.EOF {
			break
		}
		str.WriteString(string(line) + "\n")
	}
	//去掉最后一个换行符
	strLen := str.Len()
	if strLen > 0 {
		strLen--
	}
	return str.String()[0:strLen]
}

// 读取开头范围的内容,比如10就是读取前10行
func ReadFileHeadRange(filePath string, num int) string {
	file, err := os.OpenFile(filePath, os.O_RDONLY, 0666)
	if err != nil {
		panic(err)
	}
	defer file.Close()
	r := bufio.NewReader(file)
	var str strings.Builder
	//判断长度是否越界
	if num < 1 {
		num = 1
	}
	for i := 0; i < num; i++ {
		line, _, err := r.ReadLine()
		if err == io.EOF {
			break
		}
		str.WriteString(string(line) + "\n")
	}

	//去掉最后一个换行符
	strLen := str.Len()
	if strLen > 0 {
		strLen--
	}
	return str.String()[0:strLen]

}

func ReadFileTail(filePath string, num int) string {
	file, err := os.OpenFile(filePath, os.O_RDONLY, 0666)
	if err != nil {
		panic(err)
	}
	defer file.Close()
	r := bufio.NewReader(file)
	var str strings.Builder
	var allLine []string
	for {
		line, _, err := r.ReadLine()
		if err == io.EOF {
			break
		}
		allLine = append(allLine, string(line))
	}
	//判断长度是否越界
	if num > len(allLine) {
		num = len(allLine)
	}
	if num == 0 {
		num = 1
	}
	for i := len(allLine) - num; i < len(allLine); i++ {
		str.WriteString(allLine[i] + "\n")
	}
	//去掉最后一个换行符
	strLen := str.Len()
	if strLen > 0 {
		strLen--
	}

	return str.String()[0:strLen]
}

// 读取结尾范围的内容,比如10就是读取最后10行
func ReadFileTailRange(filePath string, num int) string {
	file, err := os.OpenFile(filePath, os.O_RDONLY, 0666)
	if err != nil {
		panic(err)
	}
	defer file.Close()
	r := bufio.NewReader(file)
	var str strings.Builder
	var allLine []string
	for {
		line, _, err := r.ReadLine()
		if err == io.EOF {
			break
		}
		allLine = append(allLine, string(line))
	}
	if CheckFileEnd(file) {
		allLine = append(allLine, "")
	}
	//判断长度是否越界
	if num > len(allLine) {
		num = len(allLine)
	}
	if num == 0 {
		num = 1
	}
	for i := len(allLine) - num; i < len(allLine); i++ {
		str.WriteString(allLine[i] + "\n")
	}
	//去掉最后一个换行符
	strLen := str.Len()
	if strLen > 0 {
		strLen--
	}
	return str.String()[0:strLen]
}

// 读取文件指定行数的内容
func ReadFileLineNum(filePath string, startLine, endLine int) string {
	file, err := os.OpenFile(filePath, os.O_RDONLY, 0666)
	if err != nil {
		panic(err)
	}
	defer file.Close()
	defer file.Close()
	r := bufio.NewReader(file)
	var str strings.Builder
	var allLine []string
	for {
		line, _, err := r.ReadLine()
		if err == io.EOF {
			break
		}
		allLine = append(allLine, string(line))
	}
	//判断长度是否越界
	if startLine < 1 {
		startLine = 1
	}
	if endLine >= len(allLine) {
		endLine = len(allLine)
		if CheckFileEnd(file) {
			allLine = append(allLine, "")
			endLine++
		}
	}
	for i := startLine - 1; i < endLine; i++ {
		str.WriteString(allLine[i] + "\n")
	}
	//去掉最后一个换行符
	strLen := str.Len()
	if strLen > 0 {
		strLen--
	}
	return str.String()[0:strLen]
}

// 检查结尾是否有换行符
func CheckFileEnd(file *os.File) bool {
	//移动到文件末尾前一个字节,判断最后一个字符是否是换行符
	_, err := file.Seek(-1, 2)
	if err != nil {
		panic(err)
	}
	buf := make([]byte, 1)
	_, err = file.Read(buf)
	if err != nil {
		panic(err)
	}
	//如果最后一个字符是换行符,则添加一个空行
	if buf[0] == '\n' {
		return true
	}
	return false
}

// 字符串按行读取
func ReadLine(str string) []string {
	split := strings.Split(str, "\n")
	//清除空行和空白
	var returnData []string
	for _, v := range split {
		if strings.TrimSpace(v) != "" {
			returnData = append(returnData, strings.TrimSpace(v))
		}
	}
	return returnData
}

// 获取文件多少行
func GetFileLine(filePath string) int {
	file, err := os.Open(filePath)
	if err != nil {
		return 0
	}
	defer file.Close()

	scanner := bufio.NewScanner(file)
	count := 0
	for scanner.Scan() {
		count++
	}

	if err := scanner.Err(); err != nil {
		return 0
	}
	// 检查文件末尾是否有未计数的空行
	fileInfo, err := file.Stat()
	if err != nil {
		return 0
	}
	fileSize := fileInfo.Size()

	// 如果文件大小为0，则直接返回0行
	if fileSize == 0 {
		return 0
	}
	// 移动到文件末尾的前一个字节
	_, err = file.Seek(fileSize-1, 0)
	if err != nil {
		return 0
	}
	// 读取最后一个字节
	buf := make([]byte, 1)
	_, err = file.Read(buf)
	if err != nil {
		return 0
	}
	// 如果最后一个字节不是换行符，则表示文件末尾有未计数的一行
	if buf[0] == '\n' {
		count++
	}
	return count
}

// 计算到指定行的字节数
func GetFileLineByte(filePath string, line int) int64 {
	file, err := os.OpenFile(filePath, os.O_RDONLY, 0666)
	if err != nil {
		panic(err)
	}
	defer file.Close()
	r := bufio.NewReader(file)
	var i int
	var byteNum int64
	for {
		lineByteNum, _, err := r.ReadLine()
		if err == io.EOF {
			break
		}
		i++
		//转换为字节
		strToByte := StrToByte(string(lineByteNum))
		byteNum += int64(len(strToByte)) + 2
		if i == line {
			break
		}
	}
	if byteNum == 0 {
		return 0
	}
	return byteNum - 2
}

// 持续监听读取文件内容  (整套都是同步的,需要在外边开启一个协程)
// filePath 文件路径
// endLineNum 第一次启动后读取结尾多少行
// close 通道关闭后停止监听
// f 回调函数
// 1. string 读取的内容
// 2. int 读取的行数
// 3. int64 读取的字节数
// 4. time.Time 文件修改时间
// 5. int 文件内容变化模式 1表示文件内容变小了,2表示文件内容变大了,3表示文件内容没有变化但是修改时间变了,4表示文件内容变大了但是修改时间没有变
// 会有一个问题,如果有人中途改内容了文件没有变小, 那么读取就会有问题. 因为为了效率,只是读取了新增的追加内容,如果改的是中间的内容,那么就会有问题
func ReadFileMonitor(filePath string, endLineNum int, close chan bool, f func(string, int, int64, time.Time, int)) {
	file, err := os.Open(filePath)
	if err != nil {
		panic(err)
	}
	defer file.Close()
	//记录上次读取的位置
	var offset int64
	var lineNum int //文件总行数
	ReadFileMonitorTime(file, 1, close, func(size int64, modTime time.Time, mode int) {
		if mode == -1 { //初始状态,第一次读取文件内容
			lineNum = GetFileLine(filePath) //记录文件总行数
			//如果文件内容大于100行,则只显示最后100行
			if lineNum > endLineNum {
				offset = GetFileLineByte(filePath, GetFileLine(filePath)-endLineNum)
			}
		}
		if mode == 1 { //文件内容变小了.这种情况很少见,一般都是在监听过程中改了文件内容
			lineNum = GetFileLine(filePath) //记录文件总行数
			//如果文件内容大于100行,则只显示最后100行
			if lineNum > endLineNum {
				offset = GetFileLineByte(filePath, GetFileLine(filePath)-endLineNum)
			} else {
				offset = 0 //重新读取文件
			}
		}

		_, err = file.Seek(offset, 0)
		if err != nil {
			panic(err)
		}
		var buf bytes.Buffer
		r := bufio.NewReader(file)
		for {
			readByte, err := r.ReadByte()
			if err != nil {
				break
			}
			offset++
			buf.WriteByte(readByte)
		}
		result := buf.String()

		//如果是-1和1那么就需要增加行数
		if mode != -1 && mode != 1 {
			lineNum += strings.Count(result, "\n")
		}
		//如果长度为0,则表示文件内容没有变化
		if buf.Len() != 0 {
			//排除开头和结尾的空行
			f(result, lineNum, offset, modTime, mode)
		}
	})
}

// 几秒监控一次文件是否有变化
// file 文件
// second 几秒监控一次
// mode=1表示文件内容变小了,mode=2表示文件内容变大了,mode=3表示文件内容没有变化但是修改时间变了,mode=4表示文件内容变大了但是修改时间没有变
func ReadFileMonitorTime(file *os.File, second int, close chan bool, f func(int64, time.Time, int)) {
	//记录文件的大小和修改时间
	fileInfo, err := file.Stat()
	if err != nil {
		panic(err)
	}
	//文件大小记录
	var size = fileInfo.Size()
	//文件修改时间记录
	var modTime = fileInfo.ModTime()
	var mode = -1
	f(size, modTime, mode) //先执行一次
	for {
		select {
		case <-close: //如果关闭了通道,结束监听
			return
		default:
			//等等几秒
			time.Sleep(time.Duration(second) * time.Second)
			fileInfoNew, err := file.Stat()
			if err != nil {
				panic(err)
			}
			//如果有变化
			if fileInfo.Size() != fileInfoNew.Size() || fileInfo.ModTime() != fileInfoNew.ModTime() {
				//记录文件的大小和修改时间
				var newSize = fileInfoNew.Size()
				var newModTime = fileInfoNew.ModTime()
				//如果文件内容变小了mode=1
				if newSize < size {
					mode = 1
				}
				if newSize > size { //如果文件内容变大了mode=2
					mode = 2
				}
				//修改时间变了但是文件大小没有变(这种情况就是啥也没有改,只是保存了一下文件)
				if newModTime != modTime && newSize == size {
					mode = 3
				}
				//修改时间没有变但是文件大小变了(这种情况很少见,一般都是恶意修改文件的修改时间)
				if newModTime == modTime && newSize != size {
					mode = 4
				}
				size = newSize
				modTime = newModTime
				fileInfo = fileInfoNew
				//执行函数
				f(size, modTime, mode)
			}
		}

	}

}

// 文件按行读取,返回[]string
func ReadFileLine(filePath string) []string {
	file, err := os.OpenFile(filePath, os.O_RDONLY, 0666)
	if err != nil {
		panic(err)
	}
	defer file.Close()
	var returnData []string
	r := bufio.NewReader(file)
	for {
		line, _, err := r.ReadLine()
		if err == io.EOF {
			break
		}
		returnData = append(returnData, string(line))
	}
	return returnData
}

// 文件按行读取,func(string) error
func ReadFileLineF(filePath string, f func(string) error) {
	file, err := os.OpenFile(filePath, os.O_RDONLY, 0666)
	if err != nil {
		panic(err)
	}

	defer file.Close()
	r := bufio.NewReader(file)
	for {

		line, _, err := r.ReadLine()
		if err == io.EOF {
			break
		}
		err = f(string(line))
		if err != nil {
			panic(err)
		}
	}
}

func ReadFileByteAll(filePath string) []byte {
	file, err := os.OpenFile(filePath, os.O_RDONLY, 0666)
	if err != nil {
		panic(err)
	}
	defer file.Close()
	b, err := io.ReadAll(file)
	if err != nil {
		panic(err)
	}
	return b
}

/*
*
  - 读取文件内容
  - @param  {[string]} filePath string [文件路径]
  - @param  {[int64]} offset   int64  [偏移量]   从文件开始位置偏移offset个字节
  - @param  {[int64]} num      int64  [读取字节数]
  - @return {[string]}                 [文件内容]
*/
func ReadFileByte(filePath string, offset int64, num int64) []byte {
	file, err := os.OpenFile(filePath, os.O_RDONLY, 0666)
	if err != nil {
		panic(err)
	}
	defer file.Close()
	_, err = file.Seek(offset, 0) //0表示从文件开始位置偏移offset个字节
	if err != nil {
		panic(err)
	}
	b := make([]byte, num) //num表示读取num个字节
	_, err = file.Read(b)
	if err != nil {
		panic(err)
	}
	return b
}

// 读取到指定第一个分割符
func ReadFileButeDelim(filePath string, delim byte) []byte {
	file, err := os.OpenFile(filePath, os.O_RDONLY, 0666)
	if err != nil {
		panic(err)
	}
	defer file.Close()
	r := bufio.NewReader(file)
	b, err := r.ReadBytes(delim)
	if err != nil {
		panic(err)
	}
	return b
}

// 将字符串写入文件,覆盖内容,如果没有文件则创建
// 在写入的时候如果文件中已经存在内容,直接覆盖如果出现乱码那么就先删除文件再写入
func WriteStrFile(filePath string, str string) {
	file, err := os.OpenFile(filePath, os.O_WRONLY|os.O_CREATE, 0755)
	if err != nil {
		panic(err)
	}
	defer file.Close()
	_, err = file.WriteString(str)
	if err != nil {
		panic(err)
	}

}

// 在写入的时候如果文件中已经存在内容,直接覆盖如果出现乱码那么就先删除文件再写入
func WriteSliceFile(filePath string, slice []string) {
	file, err := os.OpenFile(filePath, os.O_WRONLY|os.O_CREATE, 0755)
	if err != nil {
		panic(err)
	}
	defer file.Close()
	var str = strings.Join(slice, "\n")
	_, err = file.WriteString(str)
	if err != nil {
		panic(err)
	}
}

// 将字节写入文件
func WriteFileByte(filePath string, isApp bool, b []byte) {
	var err error
	var file *os.File
	if isApp {
		file, err = os.OpenFile(filePath, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0755)
	} else {
		file, err = os.OpenFile(filePath, os.O_WRONLY|os.O_CREATE, 0755)
	}
	if err != nil {
		panic(err)
	}
	defer file.Close()
	_, err = file.Write(b)
	if err != nil {
		panic(err)
	}
}

// 从文件指定位置开始写入,如果存在内容则覆盖
func WriteFileByteOffset(filePath string, offset int64, b []byte) {
	file, err := os.OpenFile(filePath, os.O_WRONLY|os.O_CREATE, 0755)
	if err != nil {
		panic(err)
	}
	defer file.Close()
	_, err = file.Seek(offset, 0)
	if err != nil {
		panic(err)
	}
	_, err = file.Write(b)
	if err != nil {
		panic(err)
	}
}

/**
 *  @Description: 读写文件高级函数,可控制读写位置,可控制读写字节数,可控制读写次数
 *	@param   r_filePath string  读取文件路径
 *	@param   r_offset   int     读取位置
 *	@param   R_num      int     每次读取字节数
 *	@param   R_count    int     读取次数  ?<1表示读取到文件末尾 ,1表示读取一次,2表示读取两次,以此类推
 *  @param   r_func     func(rbyte []byte, ro *os.File, wo *os.File) []byte    读取后的字节处理函数
 *  @param   w_filePath string  写入文件路径
 *	@param   w_offset   int     写入位置 ,如果?<0表示写入到文件末尾
 */
func ReadWriteFile(
	r_filePath string, r_offset int64, R_num int64, R_count int, r_func func(rbyte []byte, ro *os.File, wo *os.File) []byte,
	w_filePath string, w_offset int64) {

	r_file, err := os.OpenFile(r_filePath, os.O_RDONLY, 0755)
	if err != nil {
		panic(err)
	}
	defer r_file.Close()
	var w_file *os.File
	if w_offset < 0 {
		w_file, err = os.OpenFile(w_filePath, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0755)
	} else {
		w_file, err = os.OpenFile(w_filePath, os.O_WRONLY|os.O_CREATE, 0755)
	}
	if err != nil {
		panic(err)
	}
	defer w_file.Close()
	//从什么位置开始读取
	_, err = r_file.Seek(r_offset, 0)
	if err != nil {
		panic(err)
	}
	//从什么位置开始写入
	if w_offset > 0 {
		_, err = w_file.Seek(w_offset, 0)
		if err != nil {
			panic(err)
		}
	}

	//如果没有传入读取后的字节处理函数,则默认不处理
	if r_func == nil {
		r_func = func(rbyte []byte, ro *os.File, wo *os.File) []byte {
			return rbyte
		}
	}

	//每次读取多少字节
	b := make([]byte, R_num)
	if R_count < 1 {
		for {
			n, r_err := r_file.Read(b)
			if r_err == io.EOF {
				break
			}
			rFunc := r_func(b[:n], r_file, w_file)
			_, w_err := w_file.Write(rFunc)
			if w_err != nil {
				panic(err)
			}
		}
	} else {
		for i := 0; i < R_count; i++ {
			n, r_err := r_file.Read(b)
			if r_err == io.EOF {
				break
			}
			rFunc := r_func(b[:n], r_file, w_file)
			_, w_err := w_file.Write(rFunc)
			if w_err != nil {
				panic(err)
			}
		}
	}
}

// 下载网络文件
func DownloadFile(url string, filePath string) {
	resp, err := http.Get(url)
	if err != nil {
		panic(err)
	}
	defer resp.Body.Close()
	file, err := os.OpenFile(filePath, os.O_WRONLY|os.O_CREATE, 0755)
	if err != nil {
		panic(err)
	}
	defer file.Close()
	_, err = io.Copy(file, resp.Body)
	if err != nil {
		panic(err)
	}
}

// 批量下载网络文件
func DownloadFiles(urls []string, filePaths []string) {
	for i, url := range urls {
		DownloadFile(url, filePaths[i])
	}
}

// 下载文件返回字节
func Download(url string) []byte {
	resp, err := http.Get(url)
	if err != nil {
		panic(err)
	}
	defer resp.Body.Close()
	bytes := make([]byte, resp.ContentLength)
	_, err = io.ReadFull(resp.Body, bytes)
	if err != nil {
		panic(err)
	}
	return bytes
}

// 上传单个文件
/**
 *  @Description: 上传单个文件
 *	@param   url       string  上传地址
 *	@param   fieldName string  上传字段名
 *	@param   filePath  string  上传文件路径
 *	@param  maps      map[string]string  上传文本字段
 */
func UploadFile(url, fieldName string, filePath string, maps map[string]string) {
	UploadFiles(url, fieldName, []string{filePath}, maps)
}

// 批量上传文件
/**
 *  @Description: 批量上传文件
 *	@param   url       string  上传地址
 *	@param   fieldName string  上传字段名
 *	@param   filePaths []string  上传文件路径
 *	@param  maps      map[string]string  上传文本字段
 */
func UploadFiles(url, fieldName string, filePaths []string, maps map[string]string) {
	body := &bytes.Buffer{}
	writer := multipart.NewWriter(body)

	//添加文本字段
	for k, v := range maps {
		addTextField(writer, k, v)
	}
	for _, filePath := range filePaths {
		addFileField(writer, fieldName, filePath)
	}
	writer.Close()
	resp, err := http.Post(url, writer.FormDataContentType(), body)
	if err != nil {
		panic(err)
	}
	defer resp.Body.Close()
	b, err := io.ReadAll(resp.Body)
	if err != nil {
		panic(err)
	}
	fmt.Println(string(b))
}
func Upload(url, fieldKey, fileName string, context []byte, maps map[string]string) {
	body := &bytes.Buffer{}
	writer := multipart.NewWriter(body)

	//添加文本字段
	for k, v := range maps {
		addTextField(writer, k, v)
	}

	part, err := writer.CreateFormFile(fieldKey, fileName)
	if err != nil {
		panic(err)
	}
	_, err = part.Write(context)
	if err != nil {
		panic(err)
	}

	writer.Close()
	resp, err := http.Post(url, writer.FormDataContentType(), body)
	if err != nil {
		panic(err)
	}
	defer resp.Body.Close()
	b, err := io.ReadAll(resp.Body)
	if err != nil {
		panic(err)
	}
	fmt.Println(string(b))

}

// addTextField 添加一个文本字段到multipart表单中
func addTextField(w *multipart.Writer, fieldname, value string) {
	fieldWriter, err := w.CreateFormField(fieldname)
	if err != nil {
		fmt.Println("Error creating form field:", err)
		return
	}
	_, err = fieldWriter.Write([]byte(value))
	if err != nil {
		fmt.Println("Error writing to form field:", err)
	}
}

// addFileField 添加一个文件字段到multipart表单中
func addFileField(w *multipart.Writer, fieldname, filename string) {
	fileWriter, err := w.CreateFormFile(fieldname, filename)
	if err != nil {
		fmt.Println("Error creating form file:", err)
		return
	}

	file, err := os.Open(filename)
	if err != nil {
		fmt.Println("Error opening file:", err)
		return
	}
	defer file.Close()

	_, err = io.Copy(fileWriter, file)
	if err != nil {
		fmt.Println("Error copying file to form file:", err)
	}
}
